frontend/pages/e/[uuid]/details.tsx (view raw)
1import moment from 'moment';
2import Button from '@mui/material/Button';
3import Box from '@mui/material/Box';
4import Link from '@mui/material/Link';
5import Paper from '@mui/material/Paper';
6import Divider from '@mui/material/Divider';
7import Container from '@mui/material/Container';
8import TextField from '@mui/material/TextField';
9import Typography from '@mui/material/Typography';
10import {useTheme} from '@mui/material/styles';
11import {DatePicker} from '@mui/x-date-pickers/DatePicker';
12import {PropsWithChildren, useState} from 'react';
13import {useTranslation} from 'react-i18next';
14import pageUtils from '../../../lib/pageUtils';
15import ShareEvent from '../../../containers/ShareEvent';
16import useEventStore from '../../../stores/useEventStore';
17import useToastStore from '../../../stores/useToastStore';
18import useSettings from '../../../hooks/useSettings';
19import EventLayout, {TabComponent} from '../../../layouts/Event';
20import {
21 EventByUuidDocument,
22 useUpdateEventMutation,
23} from '../../../generated/graphql';
24
25interface Props {
26 eventUUID: string;
27 announcement?: string;
28}
29
30const Page = (props: PropsWithChildren<Props>) => {
31 return <EventLayout {...props} Tab={DetailsTab} />;
32};
33
34const DetailsTab: TabComponent = ({}) => {
35 const {t} = useTranslation();
36 const theme = useTheme();
37 const settings = useSettings();
38 const [updateEvent] = useUpdateEventMutation();
39 const addToast = useToastStore(s => s.addToast);
40 const setEventUpdate = useEventStore(s => s.setEventUpdate);
41 const event = useEventStore(s => s.event);
42 const [isEditing, setIsEditing] = useState(false);
43 const idPrefix = isEditing ? 'EditEvent' : 'Event';
44
45 const onSave = async e => {
46 try {
47 const {uuid, ...data} = event;
48 const {id, travels, waitingPassengers, __typename, ...input} = data;
49 await updateEvent({
50 variables: {uuid, eventUpdate: input},
51 refetchQueries: ['eventByUUID'],
52 });
53 setIsEditing(false);
54 } catch (error) {
55 console.error(error);
56 addToast(t('event.errors.cant_update'));
57 }
58 };
59 const sectionSx = {
60 marginBottom: theme.spacing(2),
61 width: '540px',
62 maxWidth: '100%',
63 paddingX: theme.spacing(2),
64 };
65
66 const modifyButton = isEditing ? (
67 <Button
68 variant="contained"
69 color="primary"
70 sx={{position: 'absolute', right: theme.spacing(2)}}
71 onClick={onSave}
72 >
73 {t('event.details.save')}
74 </Button>
75 ) : (
76 <Button
77 variant="text"
78 color="primary"
79 sx={{position: 'absolute', right: theme.spacing(2)}}
80 onClick={() => setIsEditing(true)}
81 >
82 {t('event.details.modify')}
83 </Button>
84 );
85
86 if (!event) return null;
87
88 return (
89 <Box
90 sx={{
91 position: 'relative',
92 paddingLeft: '80px',
93
94 [theme.breakpoints.down('md')]: {
95 paddingLeft: 0,
96 paddingBottom: '80px',
97 },
98 }}
99 >
100 <Container maxWidth="sm" sx={{marginTop: theme.spacing(6)}}>
101 <Paper sx={{position: 'relative', padding: theme.spacing(2)}}>
102 {modifyButton}
103 <Box sx={sectionSx}>
104 <Typography variant="h6">{t('event.fields.name')}</Typography>
105 {isEditing ? (
106 <TextField
107 fullWidth
108 value={event.name}
109 onChange={e => setEventUpdate({name: e.target.value})}
110 name="name"
111 id="EditEventName"
112 />
113 ) : (
114 <Typography variant="body1" id={`${idPrefix}Name`}>
115 {event.name ?? t('event.fields.empty')}
116 </Typography>
117 )}
118 </Box>
119 <Box sx={sectionSx}>
120 <Typography variant="h6">{t('event.fields.date')}</Typography>
121 {isEditing ? (
122 <DatePicker
123 renderInput={props => (
124 <TextField
125 {...props}
126 id={`${idPrefix}Date`}
127 fullWidth
128 placeholder={t('event.fields.date_placeholder')}
129 />
130 )}
131 inputFormat="DD/MM/yyyy"
132 value={event.date}
133 onChange={date =>
134 setEventUpdate({
135 date: !date ? null : moment(date).format('YYYY-MM-DD'),
136 })
137 }
138 />
139 ) : (
140 <Typography variant="body1" id={`${idPrefix}Date`}>
141 {event.date
142 ? moment(event.date).format('DD/MM/YYYY')
143 : t('event.fields.empty')}
144 </Typography>
145 )}
146 </Box>
147 <Box sx={sectionSx}>
148 <Typography variant="h6">{t('event.fields.address')}</Typography>
149 {isEditing ? (
150 <TextField
151 fullWidth
152 multiline
153 maxRows={4}
154 inputProps={{maxLength: 250}}
155 helperText={`${event.address?.length ?? 0}/250`}
156 defaultValue={event.address}
157 value={event.address}
158 onChange={e => setEventUpdate({address: e.target.value})}
159 id={`${idPrefix}Address`}
160 name="address"
161 />
162 ) : (
163 <Typography variant="body1" id={`${idPrefix}Address`}>
164 {event.address ? (
165 <Link
166 target="_blank"
167 rel="noreferrer"
168 href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
169 event.address
170 )}`}
171 onClick={e => e.preventDefault}
172 >
173 {event.address}
174 </Link>
175 ) : (
176 t('event.fields.empty')
177 )}
178 </Typography>
179 )}
180 </Box>
181 <Box sx={sectionSx}>
182 <Typography variant="h6">
183 {t('event.fields.description')}
184 </Typography>
185 {isEditing ? (
186 <TextField
187 fullWidth
188 multiline
189 maxRows={4}
190 inputProps={{maxLength: 250}}
191 helperText={`${event.description?.length || 0}/250`}
192 defaultValue={event.description}
193 value={event.description || ''}
194 onChange={e => setEventUpdate({description: e.target.value})}
195 id={`${idPrefix}Description`}
196 name="description"
197 />
198 ) : (
199 <Typography variant="body1" id={`${idPrefix}Description`}>
200 {event.description ?? t('event.fields.empty')}
201 </Typography>
202 )}
203 </Box>
204 <Box sx={sectionSx}>
205 <Typography variant="h6">{t('event.fields.link')}</Typography>
206 <Typography>{t('event.fields.link_desc')}</Typography>
207 </Box>
208 <Box py={4} justifyContent="center" display="flex">
209 <ShareEvent
210 title={`Caroster ${event.name}`}
211 url={`${window.location.href}`}
212 />{' '}
213 </Box>
214 <Divider variant="middle" />
215 <Box pt={2} justifyContent="center" display="flex">
216 <Link href={settings?.about_link} target="_blank" rel="noopener">
217 {t('event.details.aboutCaroster')}
218 </Link>
219 </Box>
220 </Paper>
221 </Container>
222 </Box>
223 );
224};
225
226export const getServerSideProps = pageUtils.getServerSideProps(
227 async (context, apolloClient) => {
228 const {uuid} = context.query;
229 const {host = ''} = context.req.headers;
230 let event = null;
231
232 // Fetch event
233 try {
234 const {data} = await apolloClient.query({
235 query: EventByUuidDocument,
236 variables: {uuid},
237 });
238 event = data?.eventByUUID?.data;
239 } catch (error) {
240 return {
241 notFound: true,
242 };
243 }
244
245 return {
246 props: {
247 eventUUID: uuid,
248 metas: {
249 title: event?.attributes?.name || '',
250 url: `https://${host}${context.resolvedUrl}`,
251 },
252 },
253 };
254 }
255);
256export default Page;